home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 37
/
Aminet 37 (2000)(Schatztruhe)[!][Jun 2000].iso
/
Aminet
/
dev
/
cross
/
devpic.lha
/
devpic
/
source
/
picasm
/
picasm.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-02-27
|
33KB
|
1,538 lines
/*
* picasm.c
*
* Freely distributable for non-commercial use (basically,
* don't sell this program without my permission. You can
* use it for developing commercial PIC applications, but
* of course I am not responsible for any damages caused by
* this assembler generating bad code or anything like that)
*
* Copyright 1995-1998 by Timo Rossi
* See the file picasm.doc for more information
*
* email: trossi@iki.fi
* www: http://www.iki.fi/trossi/pic/
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <time.h>
#include "picasm.h"
int warnlevel;
static int total_line_count;
static int errors, warnings; /* error & warning counts */
unsigned short list_flags;
/* imported from devices.c */
extern struct pic_type pic_types[];
static FILE *list_fp;
static int listing_on;
static int list_loc;
static pic_instr_t *list_ptr;
static long list_val, list_len;
int cond_nest_count;
int unique_id_count;
/* the current source file/macro */
struct inc_file *current_file;
/* Line buffer & pointer to it */
char *line_buf_ptr;
char line_buffer[256];
static struct patch *global_patch_list;
struct patch **local_patch_list_ptr;
struct pic_type *pic_type;
int prog_mem_size;
static int reg_file_limit;
short code_generated;
static pic_instr_t prog_mem[PROGMEM_MAX];
static pic_instr_t data_eeprom[EEPROM_MAX];
static pic_instr_t pic_id[4];
pic_instr_t config_fuses;
org_mode_t O_Mode;
int prog_location; /* current address for program code */
int reg_location; /* current address for register file */
int edata_location; /* current address for data EEPROM */
int org_val;
int local_level;
/* Error handling */
/*
* Show line number/line with error message
*/
static void
err_line_ref(void)
{
struct inc_file *inc;
if(current_file != NULL) {
inc = current_file;
if(inc->type != INC_FILE) {
fprintf(stderr, "(Macro %s line %d) ",
inc->v.m.sym->name, inc->linenum);
while(inc != NULL && inc->type != INC_FILE)
inc = inc->next;
}
fprintf(stderr, "File '%s' at line %d:\n",
inc->v.f.fname, inc->linenum);
fputs(line_buffer, stderr);
if(line_buffer[0] != '\0' && line_buffer[strlen(line_buffer)-1] != '\n')
fputc('\n', stderr);
}
}
/*
* Warning message
*/
void
warning(char *fmt, ...)
{
va_list args;
err_line_ref();
fputs("Warning: ", stderr);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
if(list_fp != NULL) {
fputs("Warning: ", list_fp);
vfprintf(list_fp, fmt, args);
fputc('\n', list_fp);
}
fputc('\n', stderr);
va_end(args);
warnings++;
}
/*
* skip the end of line, in case of an error
*/
static void
error_lineskip(void)
{
write_listing_line(0);
skip_eol();
get_token();
}
/*
* Error message
* call error_lineskip() if lskip is non-zero
*
*/
void
error(int lskip, char *fmt, ...)
{
va_list args;
err_line_ref();
fputs("Error: ", stderr);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
if(list_fp != NULL) {
fputs("Error: ", list_fp);
vfprintf(list_fp, fmt, args);
fputc('\n', list_fp);
}
fputc('\n', stderr);
va_end(args);
if(++errors >= MAX_ERRORS)
fatal_error("too many errors, aborting");
if(lskip)
error_lineskip();
}
/*
* Fatal error message
*/
void
fatal_error(char *fmt, ...)
{
va_list args;
err_line_ref();
fputs("Fatal error: ", stderr);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
fputc('\n', stderr);
va_end(args);
exit(EXIT_FAILURE);
}
/* memory allocation */
void *
mem_alloc(int size)
{
void *p;
if((p = malloc(size)) == NULL)
fatal_error("Out of memory");
return p;
}
/*
* initialize the assembler
*/
static void
init_assembler(void)
{
pic_instr_t *cp;
int n;
init_symtab(); /* initialize symbol table */
/* initialize program memory to invalid values */
for(n = PROGMEM_MAX, cp = prog_mem; n-- > 0; *cp++ = INVALID_INSTR);
/* initialize data memory to invalid values */
for(n = EEPROM_MAX, cp = data_eeprom; n-- > 0; *cp++ = INVALID_DATA);
/* initialize jump patch list */
global_patch_list = NULL;
prog_location = -1;
reg_location = -1;
edata_location = -1;
org_val = -1;
current_file = NULL;
config_fuses = INVALID_CONFIG;
pic_id[0] = INVALID_ID;
errors = warnings = 0;
list_flags = 0;
list_len = 0;
listing_on = 1;
cond_nest_count = 0;
unique_id_count = 1;
total_line_count = 0;
code_generated = 0;
local_level = 0;
ifskip_mode = 0;
}
/*
* generate program code
*/
void gen_code(int val)
{
if(pic_type == NULL)
fatal_error("PIC device type not set");
if(O_Mode == O_NONE) {
O_Mode = O_PROGRAM;
if(org_val < 0) {
error(0, "ORG value not set");
prog_location = 0;
return;
}
prog_location = org_val;
} else if(O_Mode != O_PROGRAM) {
error(0, "ORG mode conflict");
O_Mode = O_PROGRAM;
return;
}
if(prog_location >= prog_mem_size)
fatal_error("Code address out of range");
if(prog_mem[prog_location] != INVALID_INSTR)
warning("Overlapping code at 0x%x\n", prog_location);
if((list_flags & LIST_PROG) == 0) {
list_loc = prog_location;
list_flags = LIST_LOC | LIST_PROG;
}
list_len++;
prog_mem[prog_location++] = val;
code_generated = 1;
}
/*
* Generate data for data EEPROM
*/
void
gen_edata(int val)
{
if(O_Mode == O_NONE) {
O_Mode = O_EDATA;
if(org_val < 0) {
error(0, "ORG value not set");
edata_location = 0;
return;
}
edata_location = org_val;
} else if(O_Mode != O_EDATA) {
error(0, "ORG mode conflict");
O_Mode = O_EDATA;
return;
}
if(edata_location >= pic_type->eeprom_size)
fatal_error("Data EEPROM address out of range");
if(data_eeprom[edata_location] < 0x100)
warning("Overlapping EEPROM data at 0x%x\n", edata_location);
if((list_flags & LIST_LOC) == 0) {
list_loc = edata_location;
list_flags = LIST_LOC | LIST_EDATA;
}
list_len++;
data_eeprom[edata_location++] = val;
code_generated = 1;
}
/*
* Write one line of Intel-hex file
*/
static void
write_hex_record(FILE *fp,
int reclen, /* length (in words) */
int loc, /* address */
pic_instr_t *data, /* pointer to word data */
int format) /* IHX8M or IHX16 */
{
int check = 0;
switch(format) {
case IHX8M:
fprintf(fp, ":%02X%04X00", 2*reclen, 2*loc);
check += ((2*loc) & 0xff) + (((2*loc) >> 8) & 0xff) + 2*reclen;
break;
case IHX16:
fprintf(fp, ":%02X%04X00", reclen, loc);
check += (loc & 0xff) + ((loc >> 8) & 0xff) + reclen;
break;
}
while(reclen--) {
switch(format) {
case IHX8M:
fprintf(fp, "%02X%02X",
(int)(*data & 0xff), (int)((*data >> 8) & 0xff));
break;
case IHX16:
fprintf(fp, "%02X%02X",
(int)((*data >> 8) & 0xff), (int)(*data & 0xff));
break;
}
check += (*data & 0xff) + ((*data >> 8) & 0xff);
data++;
}
/* write checksum, assumes 2-complement */
fprintf(fp, "%02X" HEX_EOL, (-check) & 0xff);
}
/*
* Write output file in ihx8m or ihx16-format
*
*/
static void
write_output(char *fname, int format)
{
int loc, reclen;
FILE *fp;
if((fp = fopen(fname, "w")) == NULL)
fatal_error("Can't create file '%s'", fname);
/* program */
for(loc = 0;;) {
while(loc < prog_mem_size && prog_mem[loc] == INVALID_INSTR)
loc++;
if(loc >= prog_mem_size)
break;
reclen = 0;
while(reclen < 8 && loc < prog_mem_size
&& prog_mem[loc] != INVALID_INSTR) {
loc++;
reclen++;
}
write_hex_record(fp, reclen, loc-reclen, &prog_mem[loc-reclen], format);
}
/* PIC ID */
if(pic_id[0] != INVALID_ID) {
switch(pic_type->instr_set) {
case PIC12BIT:
write_hex_record(fp, 4, prog_mem_size, pic_id, format);
break;
case PIC14BIT:
write_hex_record(fp, 4, 0x2000, pic_id, format);
break;
}
}
/* config fuses */
if(config_fuses != INVALID_CONFIG) {
write_hex_record(fp, 1, pic_type->fuse_addr, &config_fuses, format);
}
if(pic_type->eeprom_size > 0) { /* data EEPROM */
for(loc = 0;;) {
while(loc < pic_type->eeprom_size && data_eeprom[loc] >= 0x100)
loc++;
if(loc >= pic_type->eeprom_size)
break;
reclen = 0;
while(reclen < 8 && loc < pic_type->eeprom_size
&& data_eeprom[loc] < 0x100) {
loc++;
reclen++;
}
write_hex_record(fp, reclen, 0x2100+loc-reclen,
&data_eeprom[loc-reclen], format);
}
}
fputs(":00000001FF" HEX_EOL , fp); /* end record */
fclose(fp);
}
/*
* Write one line to listing file (if listing is enabled)
*/
void
write_listing_line(int cond_flag)
{
int i;
if(list_fp != NULL && listing_on) {
fprintf(list_fp, "%04d%c%c",
++total_line_count,
(current_file != NULL && current_file->type == INC_MACRO ?
'+' : ' '),
(cond_flag ? '!' : ' '));
if(line_buffer[0] != '\0') {
if(list_flags & LIST_VAL) {
fprintf(list_fp, "%08lX ", list_val);
} else {
if(list_flags & LIST_LOC)
fprintf(list_fp, "%04X", list_loc);
else
fputs(" ", list_fp);
if((list_flags & (LIST_PROG|LIST_EDATA|LIST_PTR)) != 0) {
fputc(((list_flags & LIST_FORWARD) ? '?' : ' '), list_fp);
if(list_flags & LIST_PROG) {
fprintf(list_fp, "%04X ", prog_mem[list_loc]);
} else if(list_flags & LIST_EDATA) {
fprintf(list_fp, "%04X ", data_eeprom[list_loc]);
} else if(list_flags & LIST_PTR) {
fprintf(list_fp, "%04X ", *list_ptr++);
}
} else {
fputs(" ", list_fp);
}
}
fputs(line_buffer, list_fp);
if(line_buffer[0] != '\0'
&& line_buffer[strlen(line_buffer)-1] != '\n')
fputc('\n', list_fp);
list_len--;
for(i = 0; i < list_len; i++) {
list_loc++;
fprintf(list_fp, "%04d%c ",
total_line_count,
(current_file != NULL
&& current_file->type == INC_MACRO ?
'+' : ' '));
if(list_flags & LIST_LOC)
fprintf(list_fp, "%04X", list_loc);
else
fputs(" ", list_fp);
if(list_flags & LIST_PROG) {
fprintf(list_fp, " %04X\n", prog_mem[list_loc]);
} else if(list_flags & LIST_EDATA) {
fprintf(list_fp, " %04X\n", data_eeprom[list_loc]);
} else if(list_flags & LIST_PTR) {
fprintf(list_fp, " %04X\n", *list_ptr++);
}
}
}
if(listing_on < 0)
listing_on = 0;
}
list_flags = 0;
list_len = 0;
}
/*
* parse and handle OPT-directive
* (this is special as it must be done as macro definition time
* if inside a macro)
*/
static int
handle_opt(void)
{
if(token_type != TOK_IDENTIFIER) {
error(1, "OPT syntax error");
return FAIL;
}
/*
* Note: when listing is turned off, 'listing_on' is set to -1
* here and the listing routine sets it to zero after listing
* the line containing the 'opt nol'.
*/
if(strcasecmp(token_string, "nol") == 0
|| strcasecmp(token_string, "nolist") == 0) {
listing_on = -1;
} else if(strcasecmp(token_string, "l") == 0
|| strcasecmp(token_string, "list") == 0) {
listing_on = 1;
} else {
error(1, "OPT syntax error");
return FAIL;
}
get_token();
return OK;
}
/*
* Define a macro
*/
static void
define_macro(char *name)
{
struct symbol *sym;
struct macro_line *ml;
int t;
if(token_type != TOK_NEWLINE && token_type != TOK_EOF)
error(0, "Extraneous characters after a valid source line");
skip_eol();
write_listing_line(0);
sym = add_symbol(name, SYMTAB_GLOBAL);
sym->type = SYM_MACRO;
sym->v.text = NULL;
ml = NULL;
for(;;) {
get_token(); /* read first token on next line */
t = 0;
if(token_type == TOK_IDENTIFIER) {
t = 1;
get_token();
if(token_type == TOK_COLON)
get_token();
}
if(token_type == TOK_EOF || token_type == KW_END)
fatal_error("Macro definition not terminated");
if(token_type == KW_MACRO)
fatal_error("Nested macro definitions not allowed");
if(token_type == KW_ENDM) /* end macro definition */
break;
/* OPT must be handled inside macros at definition time */
if(token_type == KW_OPT) {
get_token();
handle_opt();
} else {
if(ml == NULL) {
ml = mem_alloc(sizeof(struct macro_line)
+strlen(line_buffer));
sym->v.text = ml;
} else {
ml->next = mem_alloc(sizeof(struct macro_line)
+strlen(line_buffer));
ml = ml->next;
}
strcpy(ml->text, line_buffer);
ml->next = NULL;
}
write_listing_line(0);
line_buf_ptr = NULL;
tok_char = ' ';
}
if(t)
error(0, "Label not allowed with ENDM");
get_token();
}
/*
* Skip subroutine used by the conditional assembly directives
* return: -1=premature EOF, 0=ok, 1=label (not allowed)
*/
static int
if_else_skip(void)
{
int t, ccount;
ccount = 0;
ifskip_mode++;
do {
skip_eol();
get_token();
write_listing_line(1);
t = 0;
if(token_type == TOK_IDENTIFIER) {
t = 1;
get_token();
if(token_type == TOK_COLON)
get_token();
}
if(token_type == KW_IF) {
ccount++;
} else if(token_type == KW_ENDIF) {
if(ccount <= 0)
break;
ccount--;
} else if(token_type == KW_ELSE && ccount <= 0) {
break;
}
} while(token_type != TOK_EOF && token_type != KW_END);
ifskip_mode--;
return (token_type == TOK_EOF || token_type == KW_END) ? -1 : t;
}
/*
* Add a patch pointing to the current location
*/
void
add_patch(int tab, struct symbol *sym, patchtype_t type)
{
struct patch **patch_list_ptr, *ptch;
patch_list_ptr =
(tab == SYMTAB_GLOBAL ? &global_patch_list : local_patch_list_ptr);
if(O_Mode == O_NONE) {
O_Mode = O_PROGRAM;
if(org_val < 0) {
error(0, "ORG value not set");
prog_location = 0;
return;
}
prog_location = org_val;
}
ptch = mem_alloc(sizeof(struct patch));
ptch->label = sym;
ptch->type = type;
ptch->location = prog_location;
/* add a new patch to patch_list */
ptch->next = *patch_list_ptr;
*patch_list_ptr = ptch;
}
/*
* Apply the store patches and also free the patch list
*/
static void
apply_patches(struct patch *patch_list)
{
struct patch *ptch, *p2;
/*
* fix forward jumps/calls
*/
for(ptch = patch_list; ptch != NULL; ptch = p2) {
p2 = ptch->next;
if(ptch->label->type == SYM_FORWARD)
error(0, "Undefined label '%s%s'",
(patch_list == global_patch_list ? "" : "="),
ptch->label->name);
else
switch(ptch->type) {
case PATCH8:
if(pic_type->instr_set == PIC12BIT && (ptch->label->v.value & 0x100) != 0
&& (prog_mem[ptch->location] & 0xff00) == 0x900)
error(0, "CALL address in upper half of a page (label '%s%s')",
(patch_list == global_patch_list ? "" : "="),
ptch->label->name);
prog_mem[ptch->location] =
(prog_mem[ptch->location] & 0xff00)
| (ptch->label->v.value & 0xff);
break;
case PATCH9:
prog_mem[ptch->location] =
(prog_mem[ptch->location] & 0xfe00)
| (ptch->label->v.value & 0x1ff);
break;
case PATCH11:
prog_mem[ptch->location] =
(prog_mem[ptch->location] & 0xf800)
| (ptch->label->v.value & 0x7ff);
break;
}
mem_free(ptch);
}
}
/*
* Generate code for an instruction with 8-bit literal data
* allows forward references
*/
int
gen_byte_c(int instr_code)
{
int t, symtype;
long val;
struct symbol *sym;
t = 0;
if(token_type == TOK_IDENTIFIER || token_type == TOK_LOCAL_ID) {
symtype =
(token_type == TOK_IDENTIFIER ? SYMTAB_GLOBAL : SYMTAB_LOCAL);
if(symtype == SYMTAB_LOCAL && local_level == 0) {
error(1, "Local symbol outside a LOCAL block");
return FAIL;
}
sym = lookup_symbol(token_string, symtype);
if(sym == NULL || sym->type == SYM_FORWARD) {
if(sym == NULL) {
sym = add_symbol(token_string, symtype);
sym->type = SYM_FORWARD;
}
add_patch(symtype, sym, PATCH8);
get_token();
gen_code(instr_code);
list_flags |= LIST_FORWARD;
return OK;
}
}
val = get_expression();
if(expr_error)
return FAIL;
if(val < -0x80 || val > 0xff) {
error(0, "8-bit literal out of range");
return FAIL;
}
gen_code(instr_code | (val & 0xff));
return OK;
}
/*
* check if the current token is a valid ORG mode specifier
* and return the mode (or O_NONE if not valid mode specifier)
*/
static int
org_mode(void)
{
if(token_type == KW_EDATA)
return O_EDATA;
if(token_type == TOK_IDENTIFIER) {
if(strcasecmp(token_string, "code") == 0)
return O_PROGRAM;
if(strcasecmp(token_string, "reg") == 0)
return O_REGFILE;
}
return O_NONE;
}
/*
* The assembler itself
*/
static void
assembler(char *fname)
{
static char symname[256];
struct symbol *sym;
int op, t, symtype;
long val;
char *cp;
struct pic_type *pic;
if(pic_type != NULL) {
sprintf(symname, "__%s", pic_type->name);
sym = add_symbol(symname, SYMTAB_GLOBAL);
sym->type = SYM_DEFINED;
sym->v.value = 1;
}
begin_include(fname);
get_token();
while(token_type != TOK_EOF) {
sym = NULL;
if(token_type == TOK_IDENTIFIER || token_type == TOK_LOCAL_ID) {
symtype =
(token_type == TOK_IDENTIFIER ? SYMTAB_GLOBAL : SYMTAB_LOCAL);
if(symtype == SYMTAB_LOCAL && local_level == 0) {
error(1, "Local symbol outside a LOCAL block");
continue;
}
t = (line_buf_off == 0);
strcpy(symname, token_string);
sym = lookup_symbol(symname, symtype);
if(sym != NULL && sym->type == SYM_MACRO) {
/* skip whitespace */
while(tok_char != '\n' && isspace(tok_char))
read_src_char();
if(line_buf_ptr != NULL &&
strncasecmp(line_buf_ptr-1, "macro", 5) == 0 &&
line_buf_ptr[4] != '.' && line_buf_ptr[4] != '_' &&
!isalnum((unsigned char)line_buf_ptr[4])) {
error(1, "Multiple definition of macro '%s'", symname);
continue;
}
expand_macro(sym);
continue;
}
get_token();
switch(token_type) {
case KW_MACRO:
get_token();
if(sym != NULL) {
error(1, "Multiply defined symbol '%s%s'",
(symtype == SYMTAB_LOCAL ? "=" : ""),
sym->name);
continue;
}
define_macro(symname);
goto line_end;
case KW_EQU:
if(sym != NULL) {
if(sym->type != SYM_FORWARD)
error(0, "Multiply defined symbol '%s%s'",
(symtype == SYMTAB_LOCAL ? "=" : ""),
sym->name);
} else
sym = add_symbol(symname, symtype);
get_token();
sym->type = SYM_DEFINED;
sym->v.value = get_expression();
if(expr_error)
continue; /* error_lineskip() done in expr.c */
list_val = sym->v.value;
list_flags = LIST_VAL;
goto line_end;
case KW_SET:
if(sym != NULL && sym->type != SYM_SET)
error(0, "Multiply defined symbol '%s%s'",
(symtype == SYMTAB_LOCAL ? "=" : ""),
sym->name);
else if(sym == NULL)
sym = add_symbol(symname, symtype);
get_token();
sym->type = SYM_SET;
sym->v.value = get_expression();
if(expr_error)
continue;
list_val = sym->v.value;
list_flags = LIST_VAL;
goto line_end;
case TOK_COLON:
get_token();
goto do_label;
default:
if(t == 0)
warning("Label not in the beginning of a line");
do_label:
switch(O_Mode) {
case O_PROGRAM:
t = prog_location;
break;
case O_REGFILE:
t = reg_location;
break;
case O_EDATA:
t = edata_location;
break;
case O_NONE:
t = org_val;
break;
}
if(t < 0) {
error(0, "ORG value not set");
} else {
if(sym != NULL && sym->type != SYM_FORWARD)
error(0, "Multiply defined symbol '%s%s'",
(symtype == SYMTAB_LOCAL ? "=" : ""),
sym->name);
if(sym == NULL)
sym = add_symbol(symname, symtype);
sym->type = SYM_DEFINED;
sym->v.value = t;
list_loc = t;
list_flags = LIST_LOC;
}
break;
}
}
/* if this line has a label, 'sym' points to it */
if(token_type == TOK_NEWLINE) {
write_listing_line(0);
get_token();
continue;
}
if(token_type == TOK_IDENTIFIER &&
(sym = lookup_symbol(token_string, SYMTAB_GLOBAL))
!= NULL && sym->type == SYM_MACRO) {
expand_macro(sym);
continue;
}
if(token_type == KW_END)
break;
if(token_type == KW_ERROR) {
while(isspace((unsigned char)(*line_buf_ptr)))
line_buf_ptr++;
error(1, "%s", line_buf_ptr);
continue;
}
op = token_type;
get_token();
switch(op) {
case KW_INCLUDE:
if(token_type != TOK_STRCONST) {
error(1, "Missing file name after INCLUDE");
continue;
}
strcpy(symname, token_string);
get_token();
if(token_type != TOK_NEWLINE && token_type != TOK_EOF)
error(0, "Extraneous characters after a valid source line");
begin_include(symname);
write_listing_line(0);
get_token();
continue;
case KW_SET:
case KW_EQU:
if(sym == NULL)
error(1, "SET/EQU without a label");
else
error(1, "SET/EQU syntax error");
continue;
case KW_MACRO:
error(1, "MACRO without a macro name");
continue;
case KW_ENDM:
error(1, "ENDM not allowed outside a macro");
continue;
case KW_EXITM:
/*
* EXITM works now (version 0.97). Strange that
* nobody noticed that it wasn't implemented
* at all in previous versions.
*/
if(current_file == NULL || current_file->type != INC_MACRO) {
error(1, "EXITM not allowed outside a macro");
continue;
}
cond_nest_count = current_file->cond_nest_count;
end_include();
break;
case KW_OPT:
if(handle_opt() != OK) {
error_lineskip();
continue;
}
break;
case KW_LOCAL:
add_local_symtab();
break;
case KW_ENDLOCAL:
if(local_level == 0) {
error(1, "ENDLOCAL without LOCAL");
continue;
}
apply_patches(*local_patch_list_ptr);
remove_local_symtab();
break;
case KW_IF:
if(sym != NULL)
error(0, "Label not allowed with IF");
val = get_expression();
if(token_type != TOK_NEWLINE && token_type != TOK_EOF)
error(0, "Extraneous characters after a valid source line");
if(val == 0) {
write_listing_line(0);
t=if_else_skip();
if(t == -1)
fatal_error("Conditional not terminated");
else if(t == 1)
error(0, "Label not allowed with %s",
(token_type == KW_ELSE ? "ELSE" : "ENDIF"));
if(token_type == KW_ELSE)
cond_nest_count++;
get_token();
goto line_end2;
} else
cond_nest_count++;
break;
case KW_ELSE:
if(sym != NULL)
error(0, "Label not allowed with %s", "ELSE");
if(current_file == NULL
|| cond_nest_count <= current_file->cond_nest_count)
error(0, "ELSE without IF");
write_listing_line(0);
t = if_else_skip();
if(t == -1)
fatal_error("Conditional not terminated");
else if(t == 1)
error(0, "Label not allowed with %s",
(token_type == KW_ELSE ? "ELSE" : "ENDIF"));
if(token_type == KW_ELSE)
error(0, "Multiple ELSE statements with one IF");
cond_nest_count--;
get_token();
goto line_end2;
case KW_ENDIF:
if(sym != NULL)
error(0, "Label not allowed with %s", "ENDIF");
if(current_file == NULL
|| cond_nest_count <= current_file->cond_nest_count)
error(0, "ENDIF without IF");
cond_nest_count--;
break;
case KW_ORG:
if(pic_type == NULL)
fatal_error("PIC device type not set");
org_val = -1;
if((t = org_mode()) != O_NONE) {
get_token();
O_Mode = (org_mode_t)t;
switch(O_Mode) {
case O_PROGRAM:
case O_NONE: /* shut up GCC warning, cannot really happen here */
org_val = prog_location;
break;
case O_REGFILE:
org_val = reg_location;
break;
case O_EDATA:
org_val = edata_location;
break;
}
if(org_val < 0) {
error(0, "ORG value not set");
O_Mode = O_NONE;
}
break;
}
val = get_expression();
if(expr_error)
continue;
if(val < 0 || val >= prog_mem_size) {
error(1, "ORG value out of range");
continue;
}
org_val = val;
O_Mode = O_NONE;
if(token_type == TOK_COMMA) {
get_token();
if((t = org_mode()) == O_NONE) {
error(0, "Invalid ORG mode");
} else {
O_Mode = (org_mode_t)t;
switch(O_Mode)
{
case O_PROGRAM:
case O_NONE: /* shut up GCC warning, cannot really happen here */
prog_location = org_val;
break;
case O_REGFILE:
reg_location = org_val;
break;
case O_EDATA:
edata_location = org_val;
break;
}
}
get_token();
}
list_loc = org_val;
list_flags = LIST_LOC;
break;
case KW_DS:
val = get_expression();
if(expr_error)
continue;
if(O_Mode == O_NONE) {
O_Mode = O_REGFILE;
if(org_val < 0) {
error(0, "ORG value not set");
reg_location = 0;
} else
reg_location = org_val;
}
if(O_Mode != O_REGFILE)
error(0, "ORG mode conflict");
else {
if(reg_location >= reg_file_limit)
fatal_error("Register file address out of range");
list_loc = reg_location;
list_flags = LIST_LOC;
reg_location += val;
}
break;
case KW_EDATA:
if(pic_type == NULL)
fatal_error("PIC device type not set");
if(pic_type->eeprom_size == 0) {
error(1, "PIC%s does not have data EEPROM", pic_type->name);
continue;
}
for(;;) {
if(token_type == TOK_STRCONST) {
for(cp = token_string; *cp != '\0'; cp++)
gen_edata((int)((unsigned char)(*cp)));
get_token();
} else {
val = get_expression();
if(expr_error)
break;
if(val < 0 || val > 0xff) {
error(0, "Data EEPROM byte out of range");
} else
gen_edata(val);
}
if(token_type != TOK_COMMA)
break;
get_token();
}
break;
case KW_CONFIG:
if(pic_type == NULL)
fatal_error("PIC device type not set");
if(config_fuses != INVALID_CONFIG) {
error(1, "Multiple CONFIG definitions");
continue;
}
parse_config();
list_flags = LIST_PTR;
list_ptr = &config_fuses;
list_len = 1;
break;
/* Device type */
case KW_DEVICE:
if(token_type != TOK_IDENTIFIER && token_type != TOK_STRCONST) {
error(1, "DEVICE requires a device type");
continue;
}
cp = token_string;
if(strncasecmp(token_string, "PIC", 3) == 0)
cp += 3;
for(pic = pic_types; pic->name != NULL; pic++) {
if(strcasecmp(pic->name, cp) == 0)
break;
}
if(pic->name == NULL)
fatal_error("Invalid PIC device type");
get_token();
if(pic_type == pic)
break;
if(pic_type != NULL) {
error(1, "Duplicate DEVICE setting");
continue;
}
pic_type = pic;
prog_mem_size = pic_type->progmem_size;
reg_file_limit = pic_type->regfile_limit;
sprintf(symname, "__%s", pic_type->name);
sym = add_symbol(symname, SYMTAB_GLOBAL);
sym->type = SYM_DEFINED;
sym->v.value = 1;
break;
/* PIC ID */
case KW_PICID:
if(pic_type == NULL)
fatal_error("PIC device type not set");
if(pic_id[0] != INVALID_ID) {
error(1, "Multiple ID definitions");
continue;
}
/*
* should check if ID is really allowed for all PIC types.
* Microchip 1994 databook specfically says that ID is not
* implemented on PIC16C6x/74... but 1995/96 databook
* has different information about that...
*/
for(t = 0;;) {
val = get_expression();
if(expr_error)
continue;
if(val < 0 || val > 0x3fff)
error(0, "PIC ID value out of range");
else {
if(t >= 4) {
error(1, "PIC ID too long (max 4 bytes)");
continue;
}
pic_id[t] = (pic_instr_t)val;
}
t++;
if(token_type != TOK_COMMA)
break;
get_token();
}
if(t > 0) {
list_flags = LIST_PTR;
list_len = t;
list_ptr = pic_id;
while(t < 4)
pic_id[t++] = 0x3fff;
}
break;
/* mnemonics */
default:
switch(pic_type->instr_set) {
case PIC12BIT:
t = assemble_12bit_mnemonic(op);
break;
case PIC14BIT:
default:
t = assemble_14bit_mnemonic(op);
break;
}
if(t != OK)
continue;
break;
}
line_end:
write_listing_line(0);
line_end2:
if(token_type == TOK_EOF)
continue;
if(token_type != TOK_NEWLINE) {
error(0, "Extraneous characters after a valid source line");
skip_eol();
}
get_token();
} /* while(token_type != TOK_EOF) */
/*
* Close all open source files
* (only really necessary if END has been used)
*/
while(current_file != NULL)
end_include();
if(local_level > 0)
error(0, "LOCAL not terminated with ENDLOCAL");
apply_patches(global_patch_list);
}
/*
* main program
*/
int
main(int argc, char *argv[])
{
static char in_filename[256], out_filename[256], list_filename[256];
static int out_format = IHX8M;
static int listing = 0, symdump = 0;
char *p;
time_t ti;
struct tm *tm;
pic_type = NULL;
out_filename[0] = '\0';
list_filename[0] = '\0';
warnlevel = 0;
while(argc > 1 && argv[1][0] == '-') {
switch(argv[1][1]) {
case 'o': /* output file name */
if(argv[1][2] != '\0')
strcpy(out_filename, &argv[1][2]);
else {
if(argc < 3) {
fputs("-o option requires a file name\n", stderr);
exit(EXIT_FAILURE);
}
strcpy(out_filename, argv[2]);
argc--;
argv++;
}
break;
case 'i': case 'I': /* output hex format (ihx8m/ihx16) */
if(strcasecmp(&argv[1][1], "ihx8m") == 0)
out_format = IHX8M;
else if(strcasecmp(&argv[1][1], "ihx16") == 0)
out_format = IHX16;
else
goto usage;
break;
case 'p': case 'P': /* PIC type */
if(!((argv[1][2] == 'i' || argv[1][2] == 'I') &&
(argv[1][3] == 'c' || argv[1][3] == 'C')))
goto usage;
for(pic_type = pic_types; pic_type->name != NULL; pic_type++) {
if(strcasecmp(pic_type->name, &argv[1][4]) == 0)
break;
}
if(pic_type->name == NULL) {
fprintf(stderr, "Invalid device type '%s'\n", &argv[1][1]);
exit(EXIT_FAILURE);
}
prog_mem_size = pic_type->progmem_size;
reg_file_limit = pic_type->regfile_limit;
break;
case 'l': /* listing/list filename */
listing = 1;
if(argv[1][2] != '\0')
strcpy(list_filename, &argv[1][2]);
break;
case 's':
symdump = 1;
break;
case 'w': /* warning mode (gives some more warnings) */
if(argv[1][2] != '\0') {
warnlevel = atoi(&argv[1][2]);
} else {
warnlevel = 1;
}
break;
case 'v': /* version info */
fprintf(stderr,
"12/14-bit PIC assembler " VERSION
" -- Copyright 1995-1998 by Timo Rossi\n"
"AmigaOS version beta2 by RDC <rdc@cch.pmc.ru>\n");
break;
case '-': /* end of option list */
case '\0':
argc--;
argv++;
goto opt_done;
default:
goto usage;
}
argc--;
argv++;
}
opt_done:
if(argc != 2) {
usage:
fputs("Usage: picasm [-o<objname>] [-l<listfile>] [-s] [-ihx8m/ihx16]\n"
" [-pic<device>] [-w[n]] [-version] <filename>\n", stderr);
exit(EXIT_FAILURE);
}
strncpy(in_filename, argv[1], sizeof(in_filename)-1);
if(strchr(in_filename, '.') == NULL)
strcat(in_filename, ".asm");
if(out_filename[0] == '\0') {
strcpy(out_filename, in_filename);
if((p = strrchr(out_filename, '.')) != NULL)
*p = '\0';
}
if(strchr(out_filename, '.') == NULL)
strcat(out_filename, ".hex");
init_assembler();
list_fp = NULL;
if(listing) {
if(list_filename[0] == '\0') {
strcpy(list_filename, in_filename);
if((p = strrchr(list_filename, '.')) != NULL)
*p = '\0';
strcat(list_filename, ".lst");
}
if((list_fp = fopen(list_filename, "w")) == NULL)
fatal_error("Can't create listing file '%s'", list_filename);
ti = time(NULL);
tm = localtime(&ti);
fprintf(list_fp, "** 12/14-bit PIC assembler " VERSION "\n");
fprintf(list_fp, "** %s assembled %s\n",
in_filename, asctime(tm));
}
assembler(in_filename);
if(errors == 0) {
if(code_generated)
write_output(out_filename, out_format);
else
fputs("No code generated\n", stderr);
}
else
fprintf(stderr, "%d error%s found\n", errors, errors == 1 ? "" : "s");
if(warnings != 0)
fprintf(stderr, "%d warning%s\n", warnings, warnings == 1 ? "" : "s");
if(list_fp)
{
if(symdump)
dump_symtab(list_fp);
fclose(list_fp);
}
return EXIT_SUCCESS;
}